BearZPY Blog

Hi, nice to meet you

BearZPY's avatar BearZPY

自定义 View 绘制基础

自定义 View

自定义控件继承于 View,我们按自己的需要复写其构造、onMeasure、onLayout、onTouchEvent、onDraw、onAttachedToWindow、onDetachedFromWindow 等方法。其中 onMeasure、onLayout、onDraw 这三个方法是 View 的核心方法。本篇主要涉及到 onDraw() 使用的 Canvas 的 drawXXX() 系列方法及 Paint 常用方法。

View 的构造函数

View 的构造函数有 4 个,一般情况下只要实现前三个。

// 属性优先级:XML 定义 > XML 的 Style 定义 > defStyleAttr > defStyleRes > Theme
public class MyView extends View {

    // 代码中创建 View 时使用
    public MyView(Context context) {
        super(context);
    }

    // 从 XML 加载 View 时使用
    // AttributeSet: 属性集,XML 内设置的内容
    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    // 主题中定义 View 的样式时使用
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 获取自定义属性
    }

    // 当 defStyleAttr 为 0 时使用
    // 最小 API 21
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

}

onDraw() 绘制基础

自定义绘制的方式是重写绘制方法,其中最常用的是 onDraw()。我们需要提前创建好 Paint 对象,重写 onDraw() 方法,编写具体的绘制代码。在 Android 里,每个 View 都有一个自己的坐标系,彼此之间是不影响的。这个坐标系的原点是 View 左上角的那个点;水平方向是 x 轴,右正左负;竖直方向是 y 轴,下正上负。

    Paint paint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 在坐标 (100, 100) 处画一段文字
        canvas.drawText("Hello World!",100, 100, paint);
    }

Canvas 常用方法

  1. drawColor(@ColorInt int color)

    颜色填充,在整个绘制区域统一涂上指定的颜色。
    注:drawRGB,drawARGB 也能达到这个效果,一般用于在绘制之前设置底色,或者在绘制之后为界面设置半透明蒙版。

  2. drawCircle(float centerX, float centerY, float radius, Paint paint)

    画圆,centerX centerY 是圆心的坐标,第三个参数 radius 是圆的半径,单位都是像素,paint 提供基本信息之外的所有风格信息,例如颜色、线条粗细、阴影等。

  3. drawRect(float left, float top, float right, float bottom, Paint paint)

    画矩形,left, top, right, bottom 是矩形四条边的坐标。还有 drawRect(RectF rect, Paint paint) 和 drawRect(Rect rect, Paint paint) 两个重载方法。

  4. drawPoint(float x, float y, Paint paint)

    画点,x,y 是点的坐标,点的大小可以通过 paint.setStrokeWidth(width) 来设置;点的形状可以通过 paint.setStrokeCap(cap) 来设置。

  5. drawOval(float left, float top, float right, float bottom, Paint paint)

    画椭圆,只能绘制横着的或者竖着的椭圆,不能绘制斜的,left, top, right, bottom 是这个椭圆的左、上、右、下四个边界点的坐标。重载方法 drawOval(RectF rect, Paint paint)。

  6. drawLine(float startX, float startY, float stopX, float stopY, Paint paint)

    画线,startX, startY, stopX, stopY 分别是线的起点和终点坐标。drawLines(float[] pts, int offset, int count, Paint paint) / drawLines(float[] pts, Paint paint) 画线(批量)。

  7. drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)

    画圆角矩形,重载方法 drawRoundRect(RectF rect, float rx, float ry, Paint paint)。

  8. drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

    绘制弧形或扇形,drawArc() 是使用一个椭圆来描述弧形的。left, top, right, bottom 描述的是这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。

  9. drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

    画 Bitmap,left 和 top 是要把 bitmap 绘制到的位置坐标。

  10. drawText(String text, float x, float y, Paint paint) 绘制文字

    x 和 y 是绘制的起点坐标。

Canvas.drawPath() 自定义图形

drawPath() 可以绘制自定义图形,用于常用方法满足不了的时候,画出自定义图形。Path 有两类方法,一类是直接描述路径的,另一类是辅助的设置或计算。

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Path path = new Path();

    {
        path.addCircle(200, 200, 100, Path.Direction.CW);
        path.addCircle(250, 250, 75, Path.Direction.CCW);
        path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(Color.GRAY);
        canvas.drawPath(path, paint);
    }

Path 直接描述路径

  1. 使用 addXxx() 添加子图形

    添加时需要指定 dir 即画笔绘制的方向。路径方向有两种:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise) 。对于普通情况,这个参数填 Path.Direction.CW 还是填 Path.Direction.CCW 没有影响。它只是在需要填充图形 (Paint.Style 为 FILL 或 FILL_AND_STROKE) ,并且图形出现自相交时,用于判断填充范围的。
    eg:addCircle(float x, float y, float radius, Direction dir) 添加圆。

  2. 使用 xxxTo() 画线(直线或曲线)

    eg:lineTo(float x, float y) / rLineTo(float x, float y) 画直线,从当前位置向目标位置画一条直线, x 和 y 是目标位置的坐标。这两个方法的区别是,lineTo(x, y) 的参数是绝对坐标,而 rLineTo(x, y) 的参数是相对当前位置的相对坐标 (前缀 r 指的就是 relatively 「相对地」)。当前位置:所谓当前位置,即最后一次调用画 Path 的方法的终点位置。初始值为原点 (0, 0)。

  3. close() 封闭当前子图形

    它的作用是把当前的子图形封闭,即由当前位置向当前子图形的起点绘制一条直线。

  4. moveTo(float x, float y) / rMoveTo(float x, float y)

    移动到目标位置,设置图形的起点。

  5. drawPath(path, paint)

    画出图形

Path 辅助的设置或计算

  1. setFillType(Path.FillType ft)

    设置填充方式
    EVEN_ODD
    WINDING (默认值)
    INVERSE_EVEN_ODD
    INVERSE_WINDING

Paint 常用方法

  1. setColor(int color)

    用来设置绘制内容的颜色。

  2. setStyle(Paint.Style style)

    设置绘制模式:
    Paint.Style.FILL:填充模式(默认)。
    Paint.Style.STROKE:画线模式,勾边模式。
    Paint.Style.FILL_AND_STROKE:两种模式都启用。

  3. setStrokeWidth(float width)

    设置线条的宽度,STROKE 和 FILL_AND_STROKE 模式下有效。

  4. setAntiAlias(boolean aa)

    动态设置抗锯齿,也可以创建时设置 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 抗锯齿可以让边缘平滑,但也会让颜色失真。

  5. setTextSize(float textSize)

    设置文字大小。

  6. setStrokeCap(Paint.Cap cap)

    设置线条的形状:
    Paint.Cap.ROUND 圆头,笔画以半圆形突出,中心位于路径的尽头。
    Paint.Cap.SQUARE 方头,笔画以方形突出,中心位于路径的尽头。
    Paint.Cap.BUTT 平头,不超过路径结束点。